# -*- coding: utf-8 -*-
from storm.locals import *
from model import *
from MySQLdb import IntegrityError
import math
import datetime
from time import *
import sys
import traceback

class DAO:
    def __init__(self):
        self.db = create_database("mysql://iaguis:pw8837@localhost/geofinder")
        self.store = None

    def test_rows(self):
        self.store = Store(self.db)
        u1 = self.store.add(User())
        u1.email = unicode("iago@hola.com")
        u1.username = unicode("iago")
        u1.password = unicode("1be168ff837f043bde17c0314341c84271047b31")
        u1.global_points = 0
        u2 = self.store.add(User())
        u2.email = unicode("santi@hola.com")
        u2.username = unicode("santi")
        u2.password = unicode("1be168ff837f043bde17c0314341c84271047b31")
        u2.global_points = 0

        self.store.commit()

        g = self.store.add(Game())
        g.name = unicode("el juego del teto")
        g.start_time = datetime.datetime.now()
        g.finish_time = datetime.datetime(2012, 11, 15, 1, 20, 20, 232423)
        g.latitude = 16.5
        g.longitude = 20.5
        g.zone = unicode("A Coruna")
        g.max_step = 0

        self.store.commit();

        l = self.store.add(Location())
        l.name = unicode("Plaza Pontevedra")
        l.latitude = 16.7
        l.longitude = 19.2
        l.id_game = g.id

        l = self.store.add(Location())
        l.name = unicode("Juan Florez")
        l.latitude = 20
        l.longitude = 50.2
        l.id_game = g.id
        self.store.commit();

        r = self.store.add(Riddle())
        r.text = unicode("You're very close")
        r.id_location = l.id

        self.store.commit();


    def login (self, email, password):
        self.store = Store(self.db)
        self.store = Store(self.db)
        u = self.store.find(User, User.email == unicode(email)).one()
        if password == u.password:
            p = self.store.find(Participation, Participation.id_user ==
                    u.id).one()
            self.store.close()
            if p:
                return str(u.id) + ";" + str(p.id_game)
            else:
                return str(u.id)
        else:
            self.store.close()
            return None

    def register(self, email, username, password):
        self.store = Store(self.db)
        u = self.store.add(User())
        u.email = unicode(email)
        u.username = unicode(username)
        u.password = unicode(password)
        u.global_points = 0
        try:
            self.store.commit()
            self.store.close()
            return u.id
        except IntegrityError:
            self.store.close()
            return None

    def get_current_step(self, id_user):
        max_timestamp = self.store.find(Historic, And(Historic.id_user ==
            id_user, Historic.proximity == 0)).max(Historic.timestamp)
        h = self.store.find(Historic, Historic.timestamp ==
                max_timestamp).one()
        if not h:
            id_location = 1
        else:
            if h.proximity == 0:
                id_location = h.id_location + 1
            else:
                id_location = h.id_location
        return id_location

    # TODO new game?
    def join_game(self, email, password, id_game):
        self.store = Store(self.db)
        u = self.store.find(User, User.email == email).one()
        g = self.store.find(Game, Game.id == id_game).one()
        if g and u:
            p = self.store.add(Participation())
            p.id_user = u.id
            p.id_game = g.id
            p.score = 0
            p.current_riddle = 1
            try:
                self.store.commit()
            except Exception:
                self.store.close()
                return False
            self.store.close()
            return True
        self.store.close()
        return False

    def drop_game(self, email, password):
        try:
            self.store = Store(self.db)
            u = self.store.find(User, User.email == unicode(email)).one()
            p = self.store.find(Participation, Participation.id_user == u.id).one()
            l = self.store.find(Location, Location.id_game == p.id_game)

            for loc in l.__iter__():
                id_location = loc.id
                self.store.find(Historic, And(Historic.id_user == u.id,
                    Historic.id_location == id_location)).remove()

            self.store.find(Participation, Participation.id_user ==
                u.id).remove()

            self.store.commit()
            self.store.close()
            return True
        except Exception:
            return False


    def distance(self, latitude_a, longitude_a, latitude_b, longitude_b):
        self.store = Store(self.db)
        dist = math.sqrt(math.pow(latitude_a - latitude_b, 2) +
                math.pow(longitude_a - longitude_b, 2))
        if dist < 1200:
            return 0
        elif dist < 3000:
            return 1
        elif dist < 6000:
            return 2
        else:
            return 3

    def submit_location(self, email, password, latitude, longitude):
        self.store = Store(self.db)
        try:
            u = self.store.find(User, User.email == unicode(email)).one()
            p = self.store.find(Participation, Participation.id_user == u.id).one()
            id_location = self.get_current_step(u.id)

            l = self.store.find(Location, Location.id == id_location).one()
            if l:
                dist = self.distance(latitude, longitude, l.latitude, l.longitude)
                h = self.store.add(Historic())
                h.id_user = u.id
                h.id_location = id_location
                h.timestamp = datetime.datetime.now()
                h.proximity = dist
                self.store.commit()

                if dist == 0:
                    self.store.find(Participation, Participation.id_user ==
                            u.id).remove()
                    p_add = self.store.add(Participation())
                    p_add.id_user = p.id_user
                    p_add.id_game = p.id_game
                    p_add.score = p.score + 100
                    p_add.current_riddle = 1
                    nr_locations = self.store.find(Location, Location.id_game
                            == p.id_game).count()
                    self.store.commit()
                    # check if win ^^
                    if id_location == nr_locations:
                        self.drop_game(email, password)
                        return 5, None, None
                    else:
                        id_location = self.get_current_step(u.id)
                        r = self.store.find(Riddle, Riddle.id_location ==
                                id_location).order_by(Asc(Riddle.id)).first()
                        self.store.close()
                        return dist, l.name, r.text
                self.store.commit()
                self.store.close()
                return dist, None, None
        except Exception:
            self.store.close()
            return traceback.format_exc(), None, None

    def give_hints(self, email, password, next):
        self.store = Store(self.db)
        try:
            u = self.store.find(User, User.email == unicode(email)).one()
            p = self.store.find(Participation, Participation.id_user ==
                    u.id).one()
            id_location = self.get_current_step(u.id)
            l = self.store.find(Location, Location.id ==
                    id_location).one()
            r = self.store.find(Riddle, Riddle.id_location == id_location)
            nr_riddles = r.count()

            if next and p.current_riddle<nr_riddles:
                p.current_riddle+= 1

            r.config(offset=0, limit=p.current_riddle)

            riddles = []
            for riddle in r.__iter__():
                riddle_info = RiddleInfo(riddle.id, riddle.text)
                riddles.append(riddle_info)

            if riddles:
                self.store.commit()
                self.store.close()
            return riddles
        except AttributeError:
            self.store.close()
            return None

    def game_status(self, email, password):
        self.store = Store(self.db)
        try:
            u = self.store.find(User, User.email == unicode(email)).one()
            p = self.store.find(Participation, Participation.id_user ==
                    u.id).one()
            g = self.store.find(Game, Game.id == p.id_game).one()
            players = self.store.find(Participation, Participation.id_game ==
                    p.id_game)
            players.config(distinct=True)
            game_status = []
            for player in players.__iter__():
                h = self.store.find(Historic, Historic.id_user ==
                        player.id_user).one()
                u = self.store.find(User, User.id == player.id_user).one()
                if not h:
                    location = 1
                else:
                    location = h.id_location
                game_status_info = GameStatus(u.username, location,
                        player.score)
                game_status.append(game_status_info)

            self.store.close()
            return g.id, g.max_step, game_status
        except AttributeError:
            self.store.close()
            return None

    def game_notifications(self, id_user, timestamp):
        self.store = Store(self.db)
        try:
            p = self.store.find(Participation, Participation.id_user).one()
            h = self.store.find(Historic, Historic.id_user == id_user and
                    Historic.timestamp > timestamp)
            notification = []
            for updated_user in p.__iter__():
                user_historic = self.store.find(Historic, Historic.id_user ==
                        p.id_user)
                notification.append([updated_user.user.username,
                    user_historic.id_location, updated_user.score])
            self.store.close()
            return game_status
        except AttributeError:
            self.store.close()
            return None

    # TODO add crash checks
    def facade(self, email, password, offset, size):
        self.store = Store(self.db)
        u = self.store.find(User, User.email == unicode(email)).one()
        p = self.store.find(Participation, Participation.id_user == u.id).one()
        g = self.store.find(Game, True).order_by(Game.start_time)
        g.config(offset=offset, limit=size)

        games = []
        for game in g.__iter__():
            game_info = GameInfo(game.id, str(game.zone), int(mktime(game.start_time.timetuple())), self.store.find(Participation, Participation.id_game == game.id).count())
            games.append(game_info)
        self.store.close()
        return games

    def join_game(self, email, password, game_id):
        self.store = Store(self.db)
        try:
            u = self.store.find(User, User.email == unicode(email)).one()
            p = self.store.add(Participation())
            p.id_user = u.id
            p.id_game = game_id
            p.score = 0
            p.current_riddle = 1
            self.store.commit()
            self.store.close()
            return True
        except Exception:
            self.store.close()
            return False

    def global_high_scores(self, email, password):
        self.store = Store(self.db)
        try:
            u = self.store.find(User, User.email == unicode(email)).one()
            u_ordered = self.store.find(User,
                    True).order_by(Desc(User.global_points))
            u_ordered.config(limit=5)
            user_score = u.global_points

            global_scores = []
            for score in u_ordered.__iter__():
                global_scores.append(score)
            self.store.close()
            return user_score, global_scores
        except Exception:
            self.store.close()
            return False, False

    def game_scores(self, email, password):
        self.store = Store(self.db)
        try:
            u = self.store.find(User, User.email == unicode(email)).one()
            p = self.store.find(Participation, Participation.id_user ==
                    u.id).one()
            g = self.store.find(Game, Game.id == p.id_game).one()
            user_score = p.score
            game_participations = self.store.find(Participation,
                    Participation.id_game == g.id)

            game_scores = []
            for participation in game_participations.__iter__():
                id_location = self.get_current_step(participation.id_user)

                l = self.store.find(Location, Location.id == id_location).one()
                user = self.store.find(User, User.id ==
                        participation.id_user).one()

                score = GameScores(user.username, l.id, participation.score)
                game_scores.append(score)
            self.store.close()
            return user_score, game_scores
        except Exception:
            self.store.close()
            return False, False
    def last_step(self, email, password):
        self.store = Store(store.db)
        try:
            u = self.store.find(User, User.email == unicode(email)).one()

        except Exception:
            self.store.close()
            return False

    def user_path(self, email, password):
        try:
            print "TODO"
        except Exception:
            return False

